﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using iNet.IO;
using iNet.Media.Imaging.Exif;
using iNet.Tasks;

namespace iNet.Media.Imaging
{
	/// <summary>
	/// 相機 RAW 影像解碼工作基底類別。
	/// </summary>
	public abstract class CameraRawImageDecodeTask : ImageDecodeTask
	{
		#region 靜態欄位
		static readonly Regex _ImageInfoRegex = new Regex(@"^[\s]*(?<Name>[^\:]+)\:[\s]*(?<Value>.*)$", RegexOptions.IgnoreCase);
		static readonly Regex _ImageSizeRegex = new Regex(@"^(?<Width>[0-9]+)[\s]*x[\s]*(?<Height>[0-9]+)", RegexOptions.IgnoreCase);
        #endregion
		#region 欄位
		readonly List<Process> _DcrawProcesses = new List<Process>();
		ImageOrientation? _ThumbnailImageOrientation;
        #endregion
		#region 建構子
		internal CameraRawImageDecodeTask(IMediaFormat decodingFormat)
			: base(decodingFormat)
		{ }
		internal CameraRawImageDecodeTask(IMediaFormat decodingFormat, MediaStorage source, MediaDecodeRequests mediaDecodeReqs = 0, ImageDecodeRequests imageDecodeReqs = 0)
			: base(decodingFormat, source, mediaDecodeReqs, imageDecodeReqs)
		{ }
        #endregion
		#region AdjustFreeImageLuminance
		internal unsafe void AdjustFreeImageLuminance(IntPtr dib)
		{
			//確認參數
			if (dib == IntPtr.Zero)
				return;

			//取得格式
			PixelFormat pixelFormat;
			BitmapPalette palette;
			if (!FreeImage.GetFormat(dib, out pixelFormat, out palette))
				return;

			//取得大小
			int width = FreeImage.GetWidth(dib);
			int height = FreeImage.GetHeight(dib);
			int stride = FreeImage.GetStride(dib);
			if (width <= 0 || height <= 0 || stride <= 0)
				return;

			//取得緩衝區
			byte* srcPixelBuffer = (byte*)FreeImage.GetBits(dib);
			if (srcPixelBuffer == null)
				return;

			//統計亮度分佈
			long accumLuminance = 0;
			if (pixelFormat == PixelFormats.Rgb48)
			{
				byte* pixelLineBuffer = srcPixelBuffer;
				for (int y = height; y > 0; --y, pixelLineBuffer += stride)
				{
					ushort* pixelBuffer = (ushort*)pixelLineBuffer;
					for (int x = width; x > 0; --x, pixelBuffer += 3)
					{
						int r = pixelBuffer[0];
						int g = pixelBuffer[1];
						int b = pixelBuffer[2];
						int luminance = ColorConversion.ToLuminanceUnsafe(r, g, b);
						accumLuminance += luminance;
					}
				}
			}

			//建立轉換表
			bool is16BitsColor = pixelFormat.Is16BitsColor();
			int colorCount = (is16BitsColor ? 65536 : 256);
			int maxColor = (colorCount - 1);
			int middleColor = (colorCount / 2);
			double avgLuminance = (accumLuminance / (width * height));
			double brightness = Math.Min(8, ((colorCount / 2) / avgLuminance));
			double lowColorCoeff = (middleColor / avgLuminance);
			double highColoeCoeff = ((maxColor - middleColor) / (maxColor - avgLuminance));
			int[] transformTable = new int[colorCount];
			for (int i = maxColor; i >= 0; --i)
			{
				transformTable[i] = (int)(i * brightness);
				//transformTable[i] = (ushort)((i + shift) * stretch);
			}
			/*
			for (int i = (int)avgLuminance; i >= 0; --i)
				transformTable[i] = (int)(i * lowColorCoeff);
			for (int i = (int)avgLuminance + 1; i <= maxColor; ++i)
				transformTable[i] = (int)(maxColor - (maxColor - i) * highColoeCoeff);
			*/

			//套用 Gamma 轉換
			if (pixelFormat == PixelFormats.Rgb48)
			{
				byte* pixelLineBuffer = srcPixelBuffer;
				for (int y = height; y > 0; --y, pixelLineBuffer += stride)
				{
					ushort* pixelBuffer = (ushort*)pixelLineBuffer;
					for (int x = width; x > 0; --x, pixelBuffer += 3)
					{
						int r = pixelBuffer[0];
						int g = pixelBuffer[1];
						int b = pixelBuffer[2];
						int luminance = ColorConversion.ToLuminanceUnsafe(r, g, b);
						double ratio = ((double)transformTable[luminance] / luminance);
						r = (int)Math.Min(maxColor, r * ratio);
						g = (int)Math.Min(maxColor, g * ratio);
						b = (int)Math.Min(maxColor, b * ratio);
						pixelBuffer[0] = (ushort)r;
						pixelBuffer[1] = (ushort)g;
						pixelBuffer[2] = (ushort)b;
					}
				}
			}
		}
		#endregion
		#region ApplyFreeImageBrightnessTransform
		internal unsafe void ApplyFreeImageBrightnessTransform(IntPtr dib, double brightness)
		{
			//確認參數
			if (dib == IntPtr.Zero || brightness == 0 || brightness < -1 || brightness > 1)
				return;

			//取得格式
			PixelFormat pixelFormat;
			BitmapPalette palette;
			if (!FreeImage.GetFormat(dib, out pixelFormat, out palette))
				return;

			//取得大小
			int width = FreeImage.GetWidth(dib);
			int height = FreeImage.GetHeight(dib);
			int stride = FreeImage.GetStride(dib);
			if (width <= 0 || height <= 0 || stride <= 0)
				return;

			//取得緩衝區
			byte* pixelLineBuffer = (byte*)FreeImage.GetBits(dib);
			if (pixelLineBuffer == null)
				return;

			//建立轉換表
			bool is16BitsColor = pixelFormat.Is16BitsColor();
			int colorCount = (is16BitsColor ? 65536 : 256);
			int maxColor = (colorCount - 1);
			int colorDiff = (int)(brightness * maxColor);
			ushort[] transformTable = new ushort[colorCount];
			for (int i = maxColor; i >= 0; --i)
			{
				int newColor = (i + colorDiff);
				transformTable[i] = (ushort)Math.Min(maxColor, Math.Max(0, newColor));
			}

			//套用 Gamma 轉換
			if (pixelFormat == PixelFormats.Rgb48)
			{
				for (int y = height; y > 0; --y, pixelLineBuffer += stride)
				{
					ushort* pixelBuffer = (ushort*)pixelLineBuffer;
					for (int x = width; x > 0; --x, pixelBuffer += 3)
					{
						pixelBuffer[0] = transformTable[pixelBuffer[0]];
						pixelBuffer[1] = transformTable[pixelBuffer[1]];
						pixelBuffer[2] = transformTable[pixelBuffer[2]];
					}
				}
			}
		}
		#endregion
		#region ApplyFreeImageGammaTransform
		internal unsafe void ApplyFreeImageGammaTransform(IntPtr dib, double gamma)
		{
			//確認參數
			if (dib == IntPtr.Zero || gamma < 0 || gamma == 1)
				return;

			//取得格式
			PixelFormat pixelFormat;
			BitmapPalette palette;
			if (!FreeImage.GetFormat(dib, out pixelFormat, out palette))
				return;

			//取得大小
			int width = FreeImage.GetWidth(dib);
			int height = FreeImage.GetHeight(dib);
			int stride = FreeImage.GetStride(dib);
			if (width <= 0 || height <= 0 || stride <= 0)
				return;

			//取得緩衝區
			byte* pixelLineBuffer = (byte*)FreeImage.GetBits(dib);
			if (pixelLineBuffer == null)
				return;

			//建立轉換表
			bool is16BitsColor = pixelFormat.Is16BitsColor();
			int colorCount = (is16BitsColor ? 65536 : 256);
			int maxColor = (colorCount - 1);
			ushort[] transformTable = new ushort[colorCount];
			for (int i = maxColor; i >= 0; --i)
				transformTable[i] = (ushort)(Math.Pow((double)i / maxColor, gamma) * maxColor);

			//套用 Gamma 轉換
			if (pixelFormat == PixelFormats.Rgb48)
			{
				for (int y = height; y > 0; --y, pixelLineBuffer += stride)
				{
					ushort* pixelBuffer = (ushort*)pixelLineBuffer;
					for (int x = width; x > 0; --x, pixelBuffer += 3)
					{
						int r = pixelBuffer[0];
						int g = pixelBuffer[1];
						int b = pixelBuffer[2];
						int luminance = ColorConversion.ToLuminanceUnsafe(r, g, b);
						double ratio = ((double)transformTable[luminance] / luminance);
						r = (int)Math.Min(maxColor, r * ratio);
						g = (int)Math.Min(maxColor, g * ratio);
						b = (int)Math.Min(maxColor, b * ratio);
						pixelBuffer[0] = (ushort)r;
						pixelBuffer[1] = (ushort)g;
						pixelBuffer[2] = (ushort)b;
					}
				}
			}
		}
		#endregion
		#region Decode
		protected override bool Decode(TaskVersion version, MediaStorage source, MediaDecodeRequests mediaDecodeReqs, ImageDecodeRequests imageDecodeReqs)
		{
			//初始化狀態
			_ThumbnailImageOrientation = null;

			//取得資料
			int sourceLockTimeout = this.SourceLockTimeout;

			//取得來源檔案
			string fileName;
			if (!this.GetMediaStorageFile(version, source, MediaStorageFileFlags.AnsiFilePathOnly, 20, out fileName))
				throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream);

			//轉換媒體來源
			MediaStorage originalSource = source;
			source = new MediaStorage(fileName);

			//鎖定資料流
			Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

			//解碼
			try
			{
				//解碼中繼資料
				IMetadata metadata;
				if ((mediaDecodeReqs & Media.MediaDecodeRequests.DecodeMetadata) != 0)
				{
					metadata = this.DecodeMetadataInternal(stream, true);
					this.SetDecodedMetadata(version, metadata);
				}
				else
					metadata = null;

				//解碼影像大小
				int? decodeWidth = this.DecodePixelWidth;
				int? decodeHeight = this.DecodePixelHeight;
				Size<int> pixelSize;
				if ((imageDecodeReqs & Imaging.ImageDecodeRequests.DecodePixelSize) != 0 || decodeWidth != null || decodeHeight != null)
				{
					stream.Close();
					stream = null;
					try
					{
						pixelSize = this.DecodePixelSize(version, source);
					}
					finally
					{
						stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
					}
					this.SetDecodedPixelSize(version, pixelSize);
				}
				else
					pixelSize = new Size<int>();

				//解碼縮圖
				BitmapSource thumbImageSource;
				if ((mediaDecodeReqs & Media.MediaDecodeRequests.DecodeThumbnailImage) != 0)
				{
					try
					{
						thumbImageSource = this.DecodeThumbnailImageInternal(version, source, fileName, ref stream);
						this.SetDecodedThumbnailImage(version, new BitmapImage(ImageUtility.CopyBitmap(thumbImageSource, 96, 96)));
					}
					catch
					{
						thumbImageSource = null;
						this.SetDecodedThumbnailImage(version, null);
					}
				}
				else
					thumbImageSource = null;

				//解碼影像
				if ((imageDecodeReqs & Imaging.ImageDecodeRequests.DecodeImage) != 0)
				{
					//中斷確認
					if (this.IsCanceling)
						return true;

					//計算解碼大小
					bool needThumbnailImage;
					bool isThumbnailImageScaled = false;
					if (decodeWidth != null || decodeHeight != null)
					{
						//修正解碼大小
						if (decodeWidth == null)
							decodeWidth = (int)(decodeHeight.Value * (pixelSize.Width / pixelSize.Height));
						else if (decodeHeight == null)
							decodeHeight = (int)(decodeWidth * (pixelSize.Height / pixelSize.Width));
						else if (this.MaintainAspectRatio)
						{
							Size<int> size = LayoutUtility.GetRatioStretchSize(new Size<int>(decodeWidth.Value, decodeHeight.Value), pixelSize, StretchDirection.DownOnly);
							decodeWidth = (int)size.Width;
							decodeHeight = (int)size.Height;
						}

						//確認是否需要載入縮圖
						try
						{
							Size<int>? thumbSize = this.DecodeThumbnailImagePixelSizeInternal(version, source, fileName, ref stream);
							if (thumbSize != null)
								needThumbnailImage = (decodeWidth.Value <= thumbSize.Value.Width && decodeHeight.Value <= thumbSize.Value.Height);
							else
								needThumbnailImage = false;
						}
						catch
						{
							needThumbnailImage = false;
						}
					}
					else
						needThumbnailImage = true;

					//解除資料流鎖定
					stream.Close();
					stream = null;

					//載入縮圖
					if (needThumbnailImage && thumbImageSource == null)
					{
						thumbImageSource = this.DecodeImageByDcraw(version, source, decodeWidth, decodeHeight, true, true);
						isThumbnailImageScaled = true;
					}

					//中斷確認
					if (this.IsCanceling)
						return true;

					//解碼影像
					BitmapSource bitmapSource = null;
					if (decodeWidth != null || decodeHeight != null)
					{
						//使用縮圖
						if (needThumbnailImage && thumbImageSource != null)
						{
							if (isThumbnailImageScaled)
								bitmapSource = thumbImageSource;
							else
							{
								double ratioX = (decodeWidth.Value / (double)thumbImageSource.PixelWidth);
								double ratioY = (decodeHeight.Value / (double)thumbImageSource.PixelHeight);
								bitmapSource = new TransformedBitmap(thumbImageSource, new ScaleTransform(ratioX, ratioY));
							}
						}

						//直接解碼完整影像
						if (bitmapSource == null)
						{
							//bitmapSource = this.DecodeImageByFreeImage(version, source, FreeImage.LoadFlags.RawDisplay);
							bitmapSource = this.DecodeImageByDcraw(version, source, null, null, false, true);
							if (this.IsCanceling)
								return true;
							if (bitmapSource != null)
							{
								double ratioX = (decodeWidth.Value / (double)bitmapSource.PixelWidth);
								double ratioY = (decodeHeight.Value / (double)bitmapSource.PixelHeight);
								bitmapSource = new TransformedBitmap(bitmapSource, new ScaleTransform(ratioX, ratioY));
							}
						}

						//建立影像副本 (釋放原始影像)
						if (bitmapSource.DpiX != 96 || bitmapSource.DpiY != 96)
							bitmapSource = ImageUtility.CopyBitmap(bitmapSource, 96, 96);
						this.SetDecodedImage(version, new BitmapImage(null, bitmapSource, true, metadata, this.DecodingFormat));
					}
					else
					{
						//使用縮圖
						if (thumbImageSource != null)
							this.SetDecodedImage(version, new BitmapImage(null, thumbImageSource, true, metadata, this.DecodingFormat));

						//解碼完整影像
						/*
						IntPtr dib = this.DecodeFreeImageBitmap(version, source, FreeImage.LoadFlags.Default);
						this.AdjustFreeImageLuminance(dib);
						if (this.IsCanceling)
						{
							if (dib != IntPtr.Zero)
								FreeImage.Unload(dib);
							return true;
						}
						if (dib == null)
							throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
						FreeImageBitmap bitmap = new FreeImageBitmap(source, dib, metadata, this.DecodingFormat);
						this.SetDecodedImage(version, bitmap);
						*/
						bitmapSource = this.DecodeImageByDcraw(version, source, null, null, false, true);
						this.SetDecodedImage(version, new BitmapImage(null, bitmapSource, true, metadata, this.DecodingFormat));

						//中斷確認
						//if (this.IsCanceling)
						//bitmap.Dispose();
					}
				}
			}
			catch (IOException ex)
			{
				throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream, ex);
			}
			finally
			{
				//解除資料流鎖定
				if (stream != null)
					stream.Close();

				//解除檔案鎖定
				this.ReleaseMediaStorageFile(version, originalSource, fileName);
			}

			//解碼完成
			return true;
		}
		#endregion
		#region DecodeFreeImageBitmap
		internal IntPtr DecodeFreeImageBitmap(TaskVersion version, MediaStorage source, FreeImage.LoadFlags flags)
		{
			if (source.StorageType == MediaStorageType.Stream)
			{
				Stream stream;
				if (this.GetMediaStorageStream(version, source, MediaStorageStreamFlags.Readable | MediaStorageStreamFlags.Seekable, 0, out stream))
				{
					try
					{
						IntPtr dib = IntPtr.Zero;
						if (this.DoAction(new Action(() =>
						{
							dib = this.DecodeFreeImageBitmap(stream, flags);
						}), System.Threading.Timeout.Infinite))
							return dib;
						return IntPtr.Zero;
					}
					finally
					{
						this.ReleaseMediaStorageStream(version, source, stream);
					}
				}
				throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream);
			}
			else
			{
				string fileName;
				if (this.GetMediaStorageFile(version, source, 0, out fileName))
				{
					try
					{
						IntPtr dib = IntPtr.Zero;
						if (this.DoAction(new Action(() =>
						{
							dib = this.DecodeFreeImageBitmap(fileName, flags);
						}), System.Threading.Timeout.Infinite))
							return dib;
						return IntPtr.Zero;
					}
					finally
					{
						this.ReleaseMediaStorageFile(version, source, fileName);
					}
				}
				throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream);
			}
		}
		internal IntPtr DecodeFreeImageBitmap(string fileName, FreeImage.LoadFlags flags)
		{
			IntPtr dib = FreeImage.Load(FreeImage.Format.Raw, fileName, flags);
			if (dib != IntPtr.Zero)
				return this.OnFreeImageBitmapLoaded(dib, flags);
			return IntPtr.Zero;
		}
		internal IntPtr DecodeFreeImageBitmap(Stream stream, FreeImage.LoadFlags flags)
		{
			long streamPosition = stream.Position;
			try
			{
				using (FreeImageIoAdapter adapter = new FreeImageIoAdapter(stream, false))
				{
					FreeImage.FreeImageIO io = adapter.FreeImageIO;
					IntPtr dib = FreeImage.LoadFromHandle(FreeImage.Format.Raw, ref io, adapter.Handle, flags);
					if (dib != IntPtr.Zero)
						return this.OnFreeImageBitmapLoaded(dib, flags);
					return IntPtr.Zero;
				}
			}
			finally
			{
				stream.Position = streamPosition;
			}
		}
		#endregion
		#region DecodeImageByDcraw
		internal BitmapSource DecodeImageByDcraw(TaskVersion version, MediaStorage source, int? decodeWidth, int? decodeHeight, bool isThumbnail, bool useCameraWB)
		{
			//取得縮圖方向
			if (isThumbnail && _ThumbnailImageOrientation == null)
			{
				Stream stream;
				if (this.GetMediaStorageStream(version, source, 0, out stream))
				{
					try
					{
						_ThumbnailImageOrientation = this.DecodeThumbnailImageOrientationOverride(version, stream);
					}
					finally
					{
						this.ReleaseMediaStorageStream(version, source, stream);
					}
				}
			}

			//取得來源檔案
			string fileName;
			if (!this.GetMediaStorageFile(version, source, MediaStorageFileFlags.AnsiFilePathOnly, 0, out fileName))
				throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream);

			//解碼
			Process process = null;
			try
			{
				//複製輸出的 TIFF 資料
				process = this.StartDcrawProcess(string.Format("-T -6 -c{0}{1} \"{2}\"", (useCameraWB ? " -w" : ""), (isThumbnail ? " -e" : ""), fileName));
				MemoryStream memoryStream = new MemoryStream();
				this.ReadProcessOutput(process, (stream) =>
				{
					StreamCopyTask copyTask = new StreamCopyTask(stream, memoryStream);
					if (this.ExecuteChildTask(version, copyTask) != TaskState.Executed)
					{
						if (this.IsCanceling)
							return;
						throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
					}
					memoryStream.Position = 0;

				});
				if (this.IsCanceling)
					return null;

				//修正解碼大小
				if (isThumbnail && (decodeWidth != null || decodeHeight != null))
				{
					if (_ThumbnailImageOrientation != null)
					{
						switch (_ThumbnailImageOrientation.Value)
						{
							case ImageOrientation.Rotate270:
							case ImageOrientation.Rotate270FlipY:
							case ImageOrientation.Rotate90:
							case ImageOrientation.Rotate90FlipY:
								{
									int? temp = decodeHeight;
									decodeHeight = decodeWidth;
									decodeWidth = temp;
								}
								break;
						}
					}
				}

				//確認影像格式
				bool isTiff = true;
				if (memoryStream.Length > 2)
				{
					byte[] buffer = new byte[2];
					if (memoryStream.Read(buffer, 0, 2) == 2 && buffer[0] == 'P' && buffer[1] == '6')	// PPM
						isTiff = false;
					memoryStream.Position = 0;
				}
				else
					throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);

				//建立影像
				BitmapSource result;
				if (isTiff)
				{
					System.Windows.Media.Imaging.BitmapImage bitmap = new System.Windows.Media.Imaging.BitmapImage();
					bitmap.BeginInit();
					bitmap.StreamSource = memoryStream;
					bitmap.CacheOption = BitmapCacheOption.OnLoad;
					if (decodeWidth != null)
						bitmap.DecodePixelWidth = decodeWidth.Value;
					if (decodeHeight != null)
						bitmap.DecodePixelHeight = decodeHeight.Value;
					if (this.PreservePixelFormat)
						bitmap.CreateOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
					bitmap.EndInit();
					result = bitmap;
				}
				else
				{
					//解碼 PPM 影像
					using (FreeImageIoAdapter adapter = new FreeImageIoAdapter(memoryStream, false))
					{
						FreeImage.FreeImageIO io = adapter.FreeImageIO;
						IntPtr dib = FreeImage.LoadFromHandle(FreeImage.Format.Pbm, ref io, adapter.Handle);
						if (dib != IntPtr.Zero)
							result = (new FreeImageBitmap(null, dib, null, null)).ToBitmapSource();
						else
							throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
					}

					//建立縮圖
					if (decodeWidth != null || decodeHeight != null)
					{
						ScaleTransform scaleTransform = new ScaleTransform();
						if (decodeWidth != null && decodeHeight != null)
						{
							scaleTransform.ScaleX = ((double)decodeWidth.Value / result.PixelWidth);
							scaleTransform.ScaleY = ((double)decodeHeight.Value / result.PixelHeight);
						}
						else if (decodeWidth != null)
						{
							scaleTransform.ScaleX = ((double)decodeWidth.Value / result.PixelWidth);
							scaleTransform.ScaleY = scaleTransform.ScaleX;
						}
						else
						{
							scaleTransform.ScaleY = ((double)decodeHeight.Value / result.PixelHeight);
							scaleTransform.ScaleX = scaleTransform.ScaleY;
						}
						result = new TransformedBitmap(result, scaleTransform);
					}
				}

				//修正縮圖方向及解析度
				if (isThumbnail)
				{
					//修正方向
					if (_ThumbnailImageOrientation != null)
					{
						Transform transform;
						switch (_ThumbnailImageOrientation.Value)
						{
							case ImageOrientation.Rotate90:
								transform = new RotateTransform(-90);
								break;
							case ImageOrientation.Rotate180:
								transform = new RotateTransform(180);
								break;
							case ImageOrientation.Rotate270:
								transform = new RotateTransform(90);
								break;
							default:
								transform = null;
								break;
						}
						if (transform != null)
							result = ImageUtility.CopyBitmap(new TransformedBitmap(result, transform), 96, 96);
					}

					//修正解析度
					if (result.DpiX != 96 || result.DpiY != 96)
						result = ImageUtility.CopyBitmap(result, 96, 96);
				}
				return result;
			}
			catch (Exception ex)
			{
				if (ex is MediaDecodeFailedException)
					throw;
				throw new MediaDecodeFailedException(MediaDecodeFailedReason.Unknown, ex);
			}
			finally
			{
				//停止 DCRAW
				this.StopDcrawProcess(process);

				//解除媒體鎖定
				this.ReleaseMediaStorageFile(version, source, fileName);
			}
		}
		#endregion
		#region DecodeImageByFreeImage
		internal BitmapSource DecodeImageByFreeImage(TaskVersion version, MediaStorage source, FreeImage.LoadFlags flags)
		{
			IntPtr dib = this.DecodeFreeImageBitmap(version, source, flags);
			if (this.IsCanceling)
			{
				if (dib != null)
					FreeImage.Unload(dib);
				return null;
			}
			if (dib != null)
			{
				try
				{
					return FreeImage.ToBitmapSource(dib);
				}
				finally
				{
					FreeImage.Unload(dib);
				}
			}
			throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
		}
		#endregion
		#region DecodeImageOrientationOverride
		internal ImageOrientation? DecodeImageOrientationOverride(TaskVersion version, Stream stream)
		{
			TiffReader tiffReader = new TiffReader(stream);
			while (tiffReader.ReadNextTag())
			{
				if (tiffReader.Ifd == Ifd.Ifd0 && tiffReader.TagId == 274 && tiffReader.TagType == ExifTagType.UInt16)
				{
					ExifTag tag = tiffReader.Tag;
					if (tag != null && tag.Count == 1 && tag.Data != null && tag.Data.Length == 2)
						return (ImageOrientation)(int)tag.BitConverter.ToUInt16(tag.Data);
				}
			}
			return null;
		}
		#endregion
		#region DecodeMetadataInternal
		IMetadata DecodeMetadataInternal(Stream stream, bool isReadOnly)
		{
			long position = stream.Position;
			try
			{
				return this.DecodeMetadataOverride(stream, isReadOnly);
			}
			finally
			{
				stream.Position = position;
			}
		}
		#endregion
		#region DecodeMetadataOverride
		/// <summary>
		/// 解碼中繼資料。
		/// </summary>
		/// <param name="stream">影像資料流。</param>
		/// <param name="isReadOnly">是否解碼成唯讀中繼資料。</param>
		/// <returns>影像中繼資料。</returns>
		internal virtual IMetadata DecodeMetadataOverride(Stream stream, bool isReadOnly)
		{
			return new ExifImageMetadata((new TiffReader(stream)).ReadAllTags(), isReadOnly);
		}
		#endregion
		#region DecodePixelSize
		/// <summary>
		/// 解碼影像像素大小。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="source">影像媒體儲存來源。</param>
		/// <returns>影像像素大小。</returns>
		internal virtual Size<int> DecodePixelSize(TaskVersion version, MediaStorage source)
		{
			//載入標頭
			IntPtr dib = this.DecodeFreeImageBitmap(version, source, FreeImage.LoadFlags.NoPixels);

			//取得大小
			if (dib != IntPtr.Zero)
			{
				try
				{
					//取得影像方向
					ImageOrientation? imageOrientation = null;
					Stream stream;
					if (this.GetMediaStorageStream(version, source, 0, out stream))
					{
						try
						{
							imageOrientation = this.DecodeImageOrientationOverride(version, stream);
						}
						catch
						{ }
						finally
						{
							this.ReleaseMediaStorageStream(version, source, stream);
						}
					}

					//取得影像大小
					int width = FreeImage.GetWidth(dib);
					int height = FreeImage.GetHeight(dib);
					if (imageOrientation != null)
					{
						switch (imageOrientation.Value)
						{
							case ImageOrientation.Rotate90:
							case ImageOrientation.Rotate90FlipY:
							case ImageOrientation.Rotate270:
							case ImageOrientation.Rotate270FlipY:
								{
									int temp = width;
									width = height;
									height = temp;
								}
								break;
						}
					}
					return new Size<int>(width, height);
				}
				finally
				{
					FreeImage.Unload(dib);
				}
			}
			throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
		}
		#endregion
		#region DecodeThumbnailImageOrientationInternal
		ImageOrientation? DecodeThumbnailImageOrientationInternal(TaskVersion version, Stream stream)
		{
			long position = stream.Position;
			try
			{
				return this.DecodeThumbnailImageOrientationInternal(version, stream);
			}
			finally
			{
				stream.Position = position;
			}
		}
		#endregion
		#region DecodeThumbnailImageOrientationOverride
		/// <summary>
		/// 解碼縮圖影像的方向。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="stream">影像資料流。</param>
		/// <returns>影像方向，Null (Visual Basic 為 Nothing) 表示影像方向未定義。</returns>
		internal virtual ImageOrientation? DecodeThumbnailImageOrientationOverride(TaskVersion version, Stream stream)
		{
			return this.DecodeImageOrientationOverride(version, stream);
		}
		#endregion
		#region DecodeThumbnailImageInternal
		BitmapSource DecodeThumbnailImageInternal(TaskVersion version, MediaStorage source, string filePath, ref Stream stream)
		{
			//使用資料流解碼
			long position = stream.Position;
			try
			{
				BitmapSource thumb = this.DecodeThumbnailImageOverride(version, stream);
				if (thumb != null)
					return thumb;
			}
			finally
			{
				stream.Position = position;
			}

			//使用 MediaStorage 解碼
			stream.Close();
			stream = null;
			try
			{
				return this.DecodeThumbnailImageOverride(version, source);
			}
			finally
			{
				stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
				stream.Position = position;
			}
		}
		#endregion
		#region DecodeThumbnailImageOverride
		/// <summary>
		/// 解碼影像縮圖。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="source">影像儲存來源。</param>
		/// <returns>表示縮圖影像的 System.Windows.Media.Imaging.BitmapSource。</returns>
		/// <exception cref="ArgumentNullException">version 或 source 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="MediaDecodeFailedException">解碼時發生錯誤。</exception>
		internal virtual BitmapSource DecodeThumbnailImageOverride(TaskVersion version, MediaStorage source)
		{
			//確認參數
			if (version == null)
				throw new ArgumentNullException("version");
			if (source == null)
				throw new ArgumentNullException("source");

			//讀取縮圖
			BitmapSource thumbnail = this.DecodeImageByDcraw(version, source, null, null, true, false);

			//解碼完成
			return thumbnail;
		}
		/// <summary>
		/// 解碼影像縮圖。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="stream">影像資料流。</param>
		/// <returns>表示縮圖影像的 System.Windows.Media.Imaging.BitmapSource。</returns>
		internal virtual BitmapSource DecodeThumbnailImageOverride(TaskVersion version, Stream stream)
		{
			return null;
		}
		#endregion
		#region DecodeThumbnailImagePixelSizeInternal
		Size<int>? DecodeThumbnailImagePixelSizeInternal(TaskVersion version, MediaStorage source, string filePath, ref Stream stream)
		{
			//使用資料流解碼
			long position = stream.Position;
			try
			{
				Size<int>? size = this.DecodeThumbnailImagePixelSizeOverride(version, stream);
				if (size != null)
					return size;
			}
			finally
			{
				stream.Position = position;
			}

			//使用 MediaStorage 解碼
			stream.Close();
			stream = null;
			try
			{
				return this.DecodeThumbnailImagePixelSizeOverride(version, source);
			}
			finally
			{
				stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
				stream.Position = position;
			}
		}
		#endregion
		#region DecodeThumbnailImagePixelSizeOverride
		/// <summary>
		/// 解碼縮圖影像的像素大小。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="source">影像儲存來源。</param>
		/// <returns>縮圖影像的像素大小，Null (Visual Basic 為 Nothing) 表示像素大小未定義。</returns>
		/// <exception cref="ArgumentNullException">version 或 source 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="MediaDecodeFailedException">解碼時發生錯誤。</exception>
		internal virtual Size<int>? DecodeThumbnailImagePixelSizeOverride(TaskVersion version, MediaStorage source)
		{
			//確認參數
			if (version == null)
				throw new ArgumentNullException("version");
			if (source == null)
				throw new ArgumentNullException("source");

			//取得大小
			Dictionary<string, object> info = this.GetDcrawImageInfo(version, source);
			object thumbSizeObj;
			if (info.TryGetValue("Thumb size", out thumbSizeObj) && (thumbSizeObj is Size<int>))
			{
				//修正縮圖旋轉方向
				Size<int> size = (Size<int>)thumbSizeObj;
				if (_ThumbnailImageOrientation == null)
				{
					Stream stream;
					if (this.GetMediaStorageStream(version, source, 0, out stream))
					{
						try
						{
							_ThumbnailImageOrientation = this.DecodeThumbnailImageOrientationOverride(version, stream);
						}
						finally
						{
							this.ReleaseMediaStorageStream(version, source, stream);
						}
					}
				}
				if (_ThumbnailImageOrientation != null)
				{
					switch (_ThumbnailImageOrientation.Value)
					{
						case ImageOrientation.Rotate90:
						case ImageOrientation.Rotate90FlipY:
						case ImageOrientation.Rotate270:
						case ImageOrientation.Rotate270FlipY:
							size = new Size<int>(size.Height, size.Width);
							break;
					}
				}

				//解碼完成
				return size;
			}
			return null;
		}
		/// <summary>
		/// 解碼縮圖影像的像素大小。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="stream">影像資料流。</param>
		/// <returns>縮圖影像的像素大小，Null (Visual Basic 為 Nothing) 表示像素大小未定義。</returns>
		/// <exception cref="ArgumentNullException">version 或 stream 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="MediaDecodeFailedException">解碼時發生錯誤。</exception>
		internal virtual Size<int>? DecodeThumbnailImagePixelSizeOverride(TaskVersion version, Stream stream)
		{
			return null;
		}
		#endregion
		#region FreeImageBrightnessAdjustment
		internal virtual int FreeImageBrightnessAdjustment
		{
			get
			{
				return 0;
			}
		}
		#endregion
		#region GetDcrawImageInfo
		Dictionary<string, object> GetDcrawImageInfo(TaskVersion version, MediaStorage source)
		{
			//取得來源檔案
			string fileName;
			if (!this.GetMediaStorageFile(version, source, 0, out fileName))
				throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream);

			//取得影像資訊
			Process process = null;
			Dictionary<string, object> info = new Dictionary<string, object>();
			try
			{
				process = this.StartDcrawProcess(string.Format("-i -v \"{0}\"", fileName));
				Size<int> size = new Size<int>();
				this.ReadProcessOutput(process, (stream) =>
				{
					using (StreamReader reader = new StreamReader(stream, Encoding.Default))
					{
						string line = reader.ReadLine();
						while (line != null)
						{
							Match match = _ImageInfoRegex.Match(line);
							if (match.Success)
							{
								string name = match.Groups["Name"].Value;
								string value = match.Groups["Value"].Value;
								match = _ImageSizeRegex.Match(value);
								if (!match.Success)
									info[name] = value;
								else
									info[name] = new Size<int>(int.Parse(match.Groups["Width"].Value), int.Parse(match.Groups["Height"].Value));
							}
							line = reader.ReadLine();
						}
					}
				});
			}
			finally
			{
				//停止 DCRAW
				this.StopDcrawProcess(process);

				//解除媒體鎖定
				this.ReleaseMediaStorageFile(version, source, fileName);
			}
			return info;
		}
		#endregion
		#region OnCanceling
		/// <summary>
		/// 引發 Canceling 事件。
		/// </summary>
		/// <param name="e">事件資料。</param>
		protected override void OnCanceling(TaskEventArgs e)
		{
			//停止所有 DCRAW Process
			lock (_DcrawProcesses)
			{
				if (_DcrawProcesses.Count > 0)
				{
					foreach (Process process in _DcrawProcesses)
					{
						try
						{
							if (!process.HasExited)
								process.Kill();
						}
						catch
						{ }
					}
					_DcrawProcesses.Clear();
				}
			}

			//呼叫基底
			base.OnCanceling(e);
		}
		#endregion
		#region OnFreeImageBitmapLoaded
		internal virtual IntPtr OnFreeImageBitmapLoaded(IntPtr dib, FreeImage.LoadFlags flags)
		{
			if ((flags & FreeImage.LoadFlags.RawDisplay) != 0)
			{
				int adjustment = this.FreeImageBrightnessAdjustment;
				if (adjustment != 0)
					FreeImage.AdjustBrightness(dib, adjustment);
			}
			return dib;
		}
		#endregion
		#region ReadProcessOutput
		void ReadProcessOutput(Process process, Action<Stream> action)
		{
			using (Stream stream = process.StandardOutput.BaseStream)
				action(stream);
		}
		#endregion
		#region StartDcrawProcess
		Process StartDcrawProcess(string args)
		{
			//啟動 DCRAW
			Process process = new Process();
			process.StartInfo.FileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dcraw");
			process.StartInfo.Arguments = args;
			process.StartInfo.CreateNoWindow = true;
			process.StartInfo.UseShellExecute = false;
			process.StartInfo.RedirectStandardOutput = true;
			process.Start();

			//完成
			lock (_DcrawProcesses)
			{
				_DcrawProcesses.Add(process);
			}
			return process;
		}
		#endregion
		#region StopDcrawProcess
		void StopDcrawProcess(Process process)
		{
			if (process == null)
				return;
			try
			{
				if (!process.HasExited)
					process.Kill();
			}
			catch
			{ }
			lock (_DcrawProcesses)
			{
				_DcrawProcesses.Remove(process);
			}
		}
		#endregion
	}
}
